#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <pthread.h>
#include <semaphore.h>
#include <sys/stat.h>
#include <signal.h>
#include<time.h>

#define VERSION 23
#define BUFSIZE 80960
#define ERROR 42
#define LOG 44
#define FORBIDDEN 403
#define NOTFOUND 404
#define SEM_NAME "Semaphore"
#define N 1000 //每N个处理求一次平均时间

#ifndef SIGCLD
# define SIGCLD SIGCHLD
#endif
struct {
char *ext;
char *filetype;
} extensions [] = {
{"gif", "image/gif" },
{"jpg", "image/jpg" },
{"jpeg","image/jpeg"},
{"png", "image/png" },
{"ico", "image/ico" },
{"zip", "image/zip" },
{"gz", "image/gz" },
{"tar", "image/tar" },
{"htm", "text/html" },
{"html","text/html" },
{"css","text/css" },
{"js","application/x-javascript" },
{"mp4","video/mpeg4"},
{0,0},
{"apk","file"},
 };
typedef struct {
int hit;
int fd;
} webparam;

//全局变量
long memPtr[6];//各个时间存贮的数组
sem_t* psem; //diepcount信号量
int diepcount=1;//已经完成的处理请求数量

unsigned long get_file_size(const char *path)
{
unsigned long filesize = -1;
struct stat statbuff;
if(stat(path, &statbuff) < 0){
return filesize;
}else{
filesize = statbuff.st_size;
}
return filesize;
}
void logger(int type, char *s1, char *s2, int socket_fd)
{
int fd ;
char logbuffer[BUFSIZE*2];
switch (type) {
	case ERROR:
		(void) sprintf(logbuffer, "ERROR: %s:%s Errno=%d exiting pid=%d", s1,
				s2, errno, getpid());
break;
case FORBIDDEN:
		(void) write(socket_fd,
				"HTTP/1.1 403 Forbidden\nContent-Length: 185\nConnection: close\nContent-Type: text/html\n\n<html><head>\n<title>403 Forbidden</title>\n</head><body>\n<h1>Forbidden</h1>\nThe requested URL, file type or operation is not allowed on this simple static file webserver.\n</body></html>\n",
				271);
(void)sprintf(logbuffer,"FORBIDDEN: %s:%s",s1, s2);
break;
case NOTFOUND:
		(void) write(socket_fd,
				"HTTP/1.1 404 Not Found\nContent-Length: 136\nConnection: close\nContent-Type: text/html\n\n<html><head>\n<title>404 Not Found</title>\n</head><body>\n<h1>Not Found</h1>\nThe requested URL was not found on this server.\n</body></html>\n",
				224);
(void)sprintf(logbuffer,"NOT FOUND: %s:%s",s1, s2);
break;
	case LOG:
		(void) sprintf(logbuffer, " INFO: %s:%s:%d", s1, s2, socket_fd);
		break;
}
/* No checks here, nothing can be done with a failure anyway */
	if ((fd = open("zcxweb.log", O_CREAT | O_WRONLY | O_APPEND, 0644)) >= 0) {
(void)write(fd,logbuffer,strlen(logbuffer));
(void)write(fd,"\n",1);
(void)close(fd);
}
	if (type == ERROR || type == NOTFOUND || type == FORBIDDEN);
}
/* this is a web thread, so we can exit on errors */
void * web(void * data)
{
int fd;
int hit;
int j, file_fd, buflen;
long i, ret, len;
char * fstr;
char buffer[BUFSIZE+1]; /* static so zero filled */
struct timespec time;
 long tm_is=0;long tm_rs=0;long tm_ws=0;long tm_of=0;long tm_log=0;
 long tm_in=0;long tm_nrs=0;long tm_nws=0;long tm_nof=0;long tm_nlog=0;
 clock_gettime(CLOCK_REALTIME, &time);
                                tm_is=time.tv_sec;tm_in=time.tv_nsec;
webparam *param=(webparam*) data;
fd=param->fd;
hit=param->hit;
                                clock_gettime(CLOCK_REALTIME, &time);///读socke计时
                                tm_rs=time.tv_sec;tm_nrs=time.tv_nsec;
	                        ret = (*(&read))(fd, buffer, BUFSIZE); /* read Web request in one go *///读取socket
				clock_gettime(CLOCK_REALTIME, &time); 
				tm_rs = (time.tv_sec - tm_rs) * 1000 * 1000 * 1000;
				tm_nrs = (time.tv_nsec - tm_nrs + tm_rs)/1000;//单位us微秒
                                memPtr[2]=(memPtr[2]+tm_nrs)/2;
	if (ret == 0 || ret == -1) { /* read failure stop now */
		logger(FORBIDDEN, "failed to read browser request", "", fd);
	}
	if (ret > 0 && ret < BUFSIZE) /* return code is valid chars */
		buffer[ret] = 0; /* terminate the buffer */
	else
		buffer[0] = 0;
	for (i = 0; i < ret; i++) /* remove CF and LF characters */
		if (buffer[i] == '\r' || buffer[i] == '\n')
			buffer[i] = '*';
	logger(LOG, "request", buffer, hit);
	if (strncmp(buffer, "GET ", 4) && strncmp(buffer, "get ", 4)) {
		logger(FORBIDDEN, "Only simple GET operation supported", buffer, fd);
	}
	for (i = 4; i < BUFSIZE; i++) { /* null terminate after the second space to ignore extra stuff */
if(buffer[i] == ' ') { /* string is "get url " +lots of other stuff */
buffer[i] = 0;
break;
}
}
for(j=0;j<i-1;j++) /* check for illegal parent directory use .. */
if(buffer[j] == '.' && buffer[j+1] == '.') {
			logger(FORBIDDEN, "Parent directory (..) path names not supported",
					buffer, fd);
}
	if (!strncmp(&buffer[0], "GET /\0", 6)
			|| !strncmp(&buffer[0], "get /\0", 6)) /* convert no filename to index file */
		(void) strcpy(buffer, "GET /index.html");
/* work out the file type and check we support it */
buflen=strlen(buffer);
fstr = (char *)0;
for(i=0;extensions[i].ext != 0;i++) {
len = strlen(extensions[i].ext);
if( !strncmp(&buffer[buflen-len], extensions[i].ext, len)) {
fstr =extensions[i].filetype;
break;
}
}
	if (fstr == 0)
		logger(FORBIDDEN, "file extension type not supported", buffer, fd);

	if ((file_fd = open(&buffer[5], O_RDONLY)) == -1) { /* open the file for reading */
		logger(NOTFOUND, "failed to open file", &buffer[5], fd);
	}
	clock_gettime(CLOCK_REALTIME, &time);///logger()计时
                                tm_log=time.tv_sec;tm_nlog=time.tv_nsec;
	                       	logger(LOG, "SEND", &buffer[5], hit);
				clock_gettime(CLOCK_REALTIME, &time); 
				tm_log = (time.tv_sec - tm_log) * 1000 * 1000 * 1000;
				tm_nlog = (time.tv_nsec - tm_nlog + tm_log)/1000;
                                memPtr[5]=(memPtr[5]+tm_nlog)/2;
clock_gettime(CLOCK_REALTIME, &time);///读网页文件信息 长度等
                                tm_of=time.tv_sec;tm_nof=time.tv_nsec;
	                        len = (long) lseek(file_fd, (off_t) 0, SEEK_END); /* lseek to the file end to find the length */
	(void) lseek(file_fd, (off_t) 0, SEEK_SET); /* lseek back to the file start ready for reading */
				clock_gettime(CLOCK_REALTIME, &time); 
				tm_of = (time.tv_sec - tm_of) * 1000 * 1000 * 1000;
				tm_nof = (time.tv_nsec - tm_nof + tm_of)/1000;
                                memPtr[4]=(memPtr[4]+tm_nof)/2;
	(void) sprintf(buffer,
			"HTTP/1.1 200 OK\nServer: zcxweb/%d.0\nContent-Length: %ld\nConnection: close\nContent-Type: %s\n\n",
			VERSION, len, fstr); /* Header + a blank line */
	logger(LOG, "Header", buffer, hit);
(void)write(fd,buffer,strlen(buffer));
                                clock_gettime(CLOCK_REALTIME, &time);///写网页socke计时
                                tm_ws=time.tv_sec;tm_nws=time.tv_nsec;
	/* send file in 8KB block - last block may be smaller */
	while ((ret = read(file_fd, buffer, BUFSIZE)) > 0) {
		(void) write(fd, buffer, ret);
	}
				clock_gettime(CLOCK_REALTIME, &time); 
				tm_ws = (time.tv_sec - tm_ws) * 1000 * 1000 * 1000;
				tm_nws = (time.tv_nsec - tm_nws + tm_ws)/1000;
                                memPtr[3]=(memPtr[3]+tm_nws)/2;
usleep(10000);/*在 socket 通道关闭前，留出一段信息发送的时间*/
close(file_fd);
close(fd);
//释放内存
free(param);
clock_gettime(CLOCK_REALTIME, &time); 
				tm_is = (time.tv_sec - tm_is) * 1000 * 1000 * 1000;
				tm_in = (time.tv_nsec - tm_in + tm_is)/1000;
          memPtr[1]=(memPtr[1]+tm_in)/2;
sem_wait(psem);
diepcount++;
sem_post(psem);
}
int main(int argc, char **argv)
{

int i, port, pid, listenfd, socketfd, hit;
socklen_t length;
static struct sockaddr_in cli_addr; /* static = initialised to zeros */
static struct sockaddr_in serv_addr; /* static = initialised to zeros */
//时间参数
	struct timespec time;
        time_t tm100p_is=0;long tm100p_in=0;//100个访问的时间
	time_t tm_is=0; long tm_in=0;//web的耗时
//创建信号量,初始信号量为 1
	if ((psem = sem_open(SEM_NAME, O_CREAT, 0666, 1)) == SEM_FAILED) {
		perror("create semaphore error");
		exit(1);
	}
     memPtr[0]=0; memPtr[1]=0; memPtr[2]=0; memPtr[3]=0; memPtr[4]=0; memPtr[5]=0;

(void) printf("argument argc pulled in is %d arg[0]=%s pid=%d\n", argc,
			argv[0], getpid() + 1);
	port = atoi(argv[1]);
	if (argc > 0)
		if (port < 0 || port > 65535) {
			(void) printf("Invalid port number (try 0->65535)\n");
			exit(0);
		}
if( argc < 3 || argc > 3 || !strcmp(argv[1], "-?") ) {
		(void) printf(
				"hint: zcxweb Port-Number Top-Directory\t\tversion %d\n\n"
						"\tzcxweb is a small and very safe mini web server\n"
						"\tzcxweb only servers out file/web pages with extensions named below\n"
						"\t and only from the named directory or its sub-directories.\n"
						"\tThere is no fancy features = safe and secure.\n\n"
						"\tExample: zcxweb 8181 /home/zcxwebdir &\n\n"
						"\tOnly Supports:", VERSION);
		for (i = 0; extensions[i].ext != 0; i++)
			(void) printf(" %s", extensions[i].ext);

		(void) printf(
				"\n\tNot Supported: URLs including \"..\", Java, Javascript, CGI\n"
						"\tNot Supported: directories / /etc /bin /lib /tmp /usr /dev /sbin \n"
						"\tNo warranty given or implied\n\tNigel Griffiths nag@uk.ibm.com\n");
		exit(0);
	}
	if (!strncmp(argv[2], "/", 2) || !strncmp(argv[2], "/etc", 5)
			|| !strncmp(argv[2], "/bin", 5) || !strncmp(argv[2], "/lib", 5)
			|| !strncmp(argv[2], "/tmp", 5) || !strncmp(argv[2], "/usr", 5)
			|| !strncmp(argv[2], "/dev", 5) || !strncmp(argv[2], "/sbin", 6)) {
		(void) printf("ERROR: Bad top directory %s, see zcxweb -?\n", argv[2]);
exit(3);
}
if(chdir(argv[2]) == -1){
(void)printf("ERROR: Can't Change to directory %s\n",argv[2]);
exit(4);
}

/* Become deamon + unstopable and no zombies children (= no wait()) */
	if (fork() != 0)
		return 0; /* parent returns OK to shell */ 
(void)signal(SIGHUP, SIG_IGN); /* ignore terminal hangups */
(void)setpgrp(); /* break away from process group */
	logger(LOG, "zcxweb starting", argv[1], getpid());
/* setup the network socket */
if((listenfd = socket(AF_INET, SOCK_STREAM,0)) <0)
logger(ERROR, "system call","socket",0);
port = atoi(argv[1]);
if(port < 0 || port >60000)
logger(ERROR,"Invalid port number (try 1->60000)",argv[1],0);
//初始化线程属性，为分离状态
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);
pthread_t pth;
serv_addr.sin_family = AF_INET;
serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
serv_addr.sin_port = htons(port);
if(bind(listenfd, (struct sockaddr *)&serv_addr,sizeof(serv_addr)) <0)
logger(ERROR,"system call","bind",0);
if( listen(listenfd,64) <0)
logger(ERROR,"system call","listen",0);
clock_gettime(CLOCK_REALTIME, &time);  //获取时间100个处理开始时间
                    tm_is=time.tv_sec;
	      tm_in=time.tv_nsec; 
for(hit=1; ;hit++) {
length = sizeof(cli_addr);
if((socketfd = accept(listenfd, (struct sockaddr *)&cli_addr, &length)) < 0)
logger(ERROR,"system call","accept",0);
webparam *param=malloc(sizeof(webparam));
param->hit=hit;
param->fd=socketfd;
if(pthread_create(&pth, &attr, &web, (void*)param)<0){
logger(ERROR,"system call","pthread_create",0);
}
if (0==diepcount%N) {
                                 clock_gettime(CLOCK_REALTIME, &time);  //获取相对于1970到现在的秒数
				tm100p_is = (time.tv_sec - tm_is) * 1000 * 1000 * 1000;//纳秒
				tm100p_in = (time.tv_nsec - tm_in + tm100p_is)/1000;// N个处理总耗时 单位微秒
                                 memPtr[0]=tm100p_in;
                                   printf("\n\nwhole %d process Totaltime： %ld us\n",N,memPtr[0]);//100个请求总处理时间
                                   printf(" process per average time： %ld us\n", memPtr[1]);//平均每个web请求处理完成时间
                                   printf(" socket send per average time： %ld us\n", memPtr[2]);//平均读socket时间
                                   printf("socket write per average time： %ld us\n", memPtr[3]);//平均写socket时间
                                   printf(" open file per average time： %ld us\n", memPtr[4]);//平均读文件时间
                                   printf("logger per average time： %ld us\n",memPtr[5]);//平均logger时间
                               //新一百次的起始时间
                   tm_is= time.tv_sec;
				tm_in=time.tv_nsec;
                       }
         }
}
